package com.stars.easyms.redis.template;

import com.alibaba.fastjson.JSON;
import com.stars.easyms.base.constant.EasyMsCommonConstants;
import com.stars.easyms.redis.properties.EasyMsRedisProperties;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redis缓存工具类
 * 方法名称：set、setnx、get对应redis的string类型，hset、hsetnx、hget对应redis的hash类型
 *
 * @author guoguifang
 * @date 2018-04-23 13:54
 * @since 1.0.0
 */
public final class EasyMsRedisTemplate {

    private final Logger logger = LoggerFactory.getLogger(EasyMsRedisTemplate.class);

    private final Map<String, RedisScript> redisScriptMap = new ConcurrentHashMap<>(64);

    private final EasyMsRedisProperties easyMsRedisProperties;

    private final RedisTemplate<String, String> redisTemplate;

    private ValueOperations<String, String> valueOperations;

    private HashOperations<String, String, String> hashOperations;

    private ListOperations<String, String> listOperations;

    private SetOperations<String, String> setOperations;

    /**
     * 将数据放入redis缓存中，如果存在则覆盖，如果不存在则设置
     *
     * @param redisKey 缓存key
     * @param value    缓存对象
     * @return true:设置成功,false:设置失败
     */
    public boolean set(String redisKey, Object value) {
        try {
            getValueOperations().set(redisKey, getValueStr(value));
            if (logger.isDebugEnabled()) {
                logger.debug("Set object to redis key({}) success!", redisKey);
            }
            return true;
        } catch (Exception e) {
            logger.error("Set object to redis key({}) failure!", redisKey, e);
        }
        return false;
    }

    /**
     * 将数据放入redis缓存中，如果存在则覆盖，如果不存在则设置
     *
     * @param redisKey 缓存key
     * @param value    缓存对象
     * @param timeout  缓存失效时间
     * @param timeUnit 失效时间单位
     * @return true:设置成功,false:设置失败
     */
    public boolean set(String redisKey, Object value, long timeout, TimeUnit timeUnit) {
        try {
            getValueOperations().set(redisKey, getValueStr(value), timeout, timeUnit);
            if (logger.isDebugEnabled()) {
                logger.debug("Set object to redis key({}) success!", redisKey);
            }
            return true;
        } catch (Exception e) {
            logger.error("Set object to redis key({}) failure!", redisKey, e);
        }
        return false;
    }

    /**
     * 将数据放入redis缓存中，如果存在则不设置，如果不存在则设置
     *
     * @param redisKey 缓存key
     * @param value    缓存对象
     * @return true:设置成功,false:设置失败/数据已存在
     */
    public boolean setIfAbsent(String redisKey, Object value) {
        try {
            Boolean result = getValueOperations().setIfAbsent(redisKey, getValueStr(value));
            if (result != null && result) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Set object to redis key({}) success!", redisKey);
                }
                return true;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Set object to redis key({}) failure, because has been in existence!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Set object to redis key({}) failure!", redisKey, e);
        }
        return false;
    }

    /**
     * 将数据放入redis缓存中，如果存在则不设置，如果不存在则设置
     *
     * @param redisKey 缓存key
     * @param value    缓存对象
     * @param timeout  缓存失效时间
     * @param timeUnit 失效时间单位
     * @return true:设置成功,false:设置失败/数据已存在
     */
    public boolean setIfAbsent(String redisKey, Object value, long timeout, TimeUnit timeUnit) {
        try {
            Boolean result = getValueOperations().setIfAbsent(redisKey, getValueStr(value), timeout, timeUnit);
            if (result != null && result) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Set object to redis key({}) success!", redisKey);
                }
                return true;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Set object to redis key({}) failure, because has been in existence!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Set object to redis key({}) failure!", redisKey, e);
        }
        return false;
    }

    /**
     * 从redis缓存中查询，反序列化
     *
     * @param redisKey 缓存key
     * @param clazz    缓存value的类型
     * @return 缓存对象
     */
    public <T> T get(String redisKey, Class<T> clazz) {
        return parseObject(get(redisKey), clazz);
    }

    /**
     * 从redis缓存中查询，反序列化
     *
     * @param redisKey 缓存key
     * @param clazz    list中保存的对象类型
     * @return 缓存对象
     */
    public <T> List<T> getArray(String redisKey, Class<T> clazz) {
        return parseArray(get(redisKey), clazz);
    }

    /**
     * 从redis缓存中查询，反序列化
     *
     * @param redisKey 缓存key
     * @return 缓存对象
     */
    public String get(String redisKey) {
        try {
            String result = getValueOperations().get(redisKey);
            if (StringUtils.isNotEmpty(result)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Find object from redis key({}) success!", redisKey);
                }
                return result;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Did't find object from redis key({})!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Find object from redis key({}) failure!", redisKey, e);
        }
        return null;
    }

    /**
     * 在redis中查询redisKey是否存在
     *
     * @param redisKey 缓存key
     * @return 缓存对象
     */
    public boolean hasKey(String redisKey) {
        try {
            Boolean result = redisTemplate.hasKey(redisKey);
            return result != null && result;
        } catch (Exception e) {
            logger.error("Find object from redis key({}) failure!", redisKey, e);
        }
        return false;
    }

    /**
     * 设置缓存失效时间
     *
     * @param redisKey 缓存key
     * @param timeout  失效时间
     * @param timeUnit 时间单位
     * @return true:设置成功,false:设置失败
     */
    public boolean expire(String redisKey, long timeout, TimeUnit timeUnit) {
        try {
            Boolean result = redisTemplate.expire(redisKey, timeout, timeUnit);
            if (result != null && result) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Set redis key({}) expire time success!", redisKey);
                }
                return true;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Set redis key({}) expire time failure, because did't find object from redis!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Set redis key({}) expire time failure!", redisKey, e);
        }
        return false;
    }

    /**
     * 获取缓存剩余过期时间
     *
     * @param redisKey 缓存key
     * @return 当 key 不存在时,返回 -2。当 key 存在但没有设置剩余生存时间时,返回 -1。否则，以秒为单位返回key的剩余生存时间。
     */
    public long getExpire(String redisKey, final TimeUnit timeUnit) {
        try {
            Long result = redisTemplate.getExpire(redisKey, timeUnit);
            if (logger.isDebugEnabled()) {
                logger.debug("Get redis key({}) remaining expire time({}) success!", redisKey, result);
            }
            return result == null ? -2 : result;
        } catch (Exception e) {
            logger.error("Get redis key({}) remaining expire time failure!", redisKey, e);
        }
        return -2;
    }

    /**
     * 根据缓存key删除Redis缓存
     *
     * @param redisKey 删除缓存key
     * @return true:删除成功,false:删除失败
     */
    public boolean delete(String redisKey) {
        try {
            Boolean result = redisTemplate.delete(redisKey);
            if (result != null && result) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Delete redis key({}) success!", redisKey);
                }
                return true;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Did't find object from redis key({})!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Delete redis key({}) failure!", redisKey, e);
        }
        return false;
    }

    /**
     * 为redisKey对应的整数值加上增量increment
     *
     * @param redisKey 缓存key
     * @param delta    增量值
     * @return 增加成功后的值，若为空则增加失败
     */
    public Long increment(String redisKey, long delta) {
        try {
            Long result = getValueOperations().increment(redisKey, delta);
            if (logger.isDebugEnabled()) {
                logger.debug("Increment redis cache key({}) success, the result is {}!", redisKey, result);
            }
            return result;
        } catch (Exception e) {
            logger.error("Increment redis cache key({}) failure!", redisKey, e);
        }
        return null;
    }

    /**
     * 为redisKey对应的浮点数值加上增量increment
     *
     * @param redisKey 缓存key
     * @param delta    增量值
     * @return 增加成功后的值，若为空则增加失败
     */
    public Double increment(String redisKey, double delta) {
        try {
            Double result = getValueOperations().increment(redisKey, delta);
            if (logger.isDebugEnabled()) {
                logger.debug("Increment redis cache key({}) success, the result is {}!", redisKey, result);
            }
            return result;
        } catch (Exception e) {
            logger.error("Increment redis cache key({}) failure!", redisKey, e);
        }
        return null;
    }

    /**
     * 添加redisKey下的子项hashKey对应的值value到redis中,如果原来有值则删除后增加即覆盖掉,如果想原来有值的时候不覆盖则使用hashSetIfAbsent()方法
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @param value    值
     * @return true:添加成功,false:添加失败
     */
    public boolean hashSet(String redisKey, String hashKey, Object value) {
        try {
            getHashOperations().put(redisKey, hashKey, getValueStr(value));
            if (logger.isDebugEnabled()) {
                logger.debug("Set object to redis key({}) hashKey({}) success!", redisKey, hashKey);
            }
            return true;
        } catch (Exception e) {
            logger.error("Set object to redis key({}) hashKey({}) failure!", redisKey, hashKey, e);
        }
        return false;
    }

    /**
     * 添加redisKey下的子项hashKey对应的值value到redis中,如果原来有值则删除后增加即覆盖掉,如果想原来有值的时候不覆盖则使用hashSetIfAbsent()方法
     *
     * @param redisKey 缓存key
     * @param obj      子项Map
     */
    @SuppressWarnings("unchecked")
    public void hashSet(String redisKey, Object obj) {
        if (obj != null) {
            Map map;
            if (obj instanceof Map) {
                map = (Map) obj;
            } else if (obj instanceof String) {
                map = JSON.parseObject((String) obj);
            } else {
                map = JSON.parseObject(JSON.toJSONString(obj));
            }
            if (!map.isEmpty()) {
                map.forEach((hashKey, value) -> hashSet(redisKey, getValueStr(hashKey), value));
            }
        }
    }

    /**
     * 添加redisKey下的子项hashKey对应的值value到redis中,如果原来有值则返回失败,如果想原来有值的时候覆盖则使用hashSet()方法
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @param value    值
     * @return true:添加成功,false:添加失败
     */
    public boolean hashSetIfAbsent(String redisKey, String hashKey, Object value) {
        try {
            boolean result = getHashOperations().putIfAbsent(redisKey, hashKey, getValueStr(value));
            if (result) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Set object to redis key({}) hashKey({}) success!", redisKey, hashKey);
                }
                return true;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Set object to redis key({}) hashKey({}) failure, because has been in existence!", redisKey, hashKey);
            }
        } catch (Exception e) {
            logger.error("Set object to redis key({}) hashKey({}) failure!", redisKey, hashKey, e);
        }
        return false;
    }

    /**
     * 添加redisKey下的子项hashKey对应的值value到redis中,如果原来有值则返回失败,如果想原来有值的时候覆盖则使用hashSet()方法
     *
     * @param redisKey 缓存key
     * @param obj      子项Map
     */
    public void hashSetIfAbsent(String redisKey, Object obj) {
        if (obj != null) {
            Map map;
            if (obj instanceof Map) {
                map = (Map) obj;
            } else if (obj instanceof String) {
                map = JSON.parseObject((String) obj);
            } else {
                map = JSON.parseObject(JSON.toJSONString(obj));
            }
            if (!map.isEmpty()) {
                map.forEach((hashKey, value) -> hashSetIfAbsent(redisKey, getValueStr(hashKey), value));
            }
        }
    }

    /**
     * 查询redisKey下的子项hashKey对应的值
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @param clazz    缓存值的类型
     * @return 查询到的值
     */
    public <T> T hashGet(String redisKey, String hashKey, Class<T> clazz) {
        return parseObject(hashGet(redisKey, hashKey), clazz);
    }

    /**
     * 查询redisKey下的子项hashKey对应的值
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @param clazz    list中保存的对象类型
     * @return 查询到的值
     */
    public <T> List<T> hashGetArray(String redisKey, String hashKey, Class<T> clazz) {
        return parseArray(hashGet(redisKey, hashKey), clazz);
    }

    /**
     * 查询redisKey下的子项hashKey对应的值
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @return 查询到的值
     */
    public String hashGet(String redisKey, String hashKey) {
        try {
            String result = getHashOperations().get(redisKey, hashKey);
            if (StringUtils.isNotEmpty(result)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Find object from redis key({}) hashKey({})!", redisKey, hashKey);
                }
                return result;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Did't find object from redis key({}) hashKey({})!", redisKey, hashKey);
            }
        } catch (Exception e) {
            logger.error("Find object from redis key({}) hashKey({}) failure!", redisKey, hashKey, e);
        }
        return null;
    }

    /**
     * 查询redisKey下的子项hashKey对应的值，当获取的hash的值类型大于一个时使用该方法
     *
     * @param redisKey   缓存key
     * @param hashKeySet 子项集合
     * @param classes    缓存值的类型数组，越靠前的类型优先级越高
     * @return 查询到的值
     */
    @NonNull
    public Map<String, Object> hashGet(String redisKey, Set<String> hashKeySet, Class<?>... classes) {
        if (hashKeySet == null || hashKeySet.isEmpty()) {
            return Collections.emptyMap();
        }
        final Map<String, Object> resultMap = new HashMap<>(hashKeySet.size());
        hashKeySet.forEach(hashKey -> resultMap.put(hashKey, parseObject(hashGet(redisKey, hashKey), classes)));
        return resultMap;
    }

    /**
     * 查询redisKey下的子项hashKey对应的值，当获取的hash的值类型等于一个时使用该方法
     *
     * @param redisKey   缓存key
     * @param hashKeySet 子项集合
     * @param clazz      缓存值的类型
     * @return 查询到的值
     */
    @NonNull
    public <T> Map<String, T> hashGet(String redisKey, Set<String> hashKeySet, Class<T> clazz) {
        if (hashKeySet == null || hashKeySet.isEmpty()) {
            return Collections.emptyMap();
        }
        final Map<String, T> resultMap = new HashMap<>(hashKeySet.size());
        hashKeySet.forEach(hashKey -> resultMap.put(hashKey, parseObject(hashGet(redisKey, hashKey), clazz)));
        return resultMap;
    }

    /**
     * 查询redisKey下的子项hashKey对应的值，当获取的hash的值类型等于一个时使用该方法
     *
     * @param redisKey   缓存key
     * @param hashKeySet 子项集合
     * @param clazz      list中保存的类型
     * @return 查询到的值
     */
    @NonNull
    public <T> Map<String, List<T>> hashGetArray(String redisKey, Set<String> hashKeySet, Class<T> clazz) {
        if (hashKeySet == null || hashKeySet.isEmpty()) {
            return Collections.emptyMap();
        }
        final Map<String, List<T>> resultMap = new HashMap<>(hashKeySet.size());
        hashKeySet.forEach(hashKey -> resultMap.put(hashKey, parseArray(hashGet(redisKey, hashKey), clazz)));
        return resultMap;
    }

    /**
     * 查询redisKey下的子项hashKey对应的值(获取的是序列化后的值)
     *
     * @param redisKey   缓存key
     * @param hashKeySet 子项集合
     * @return 查询到的值
     */
    @NonNull
    public Map<String, String> hashGet(String redisKey, Set<String> hashKeySet) {
        if (hashKeySet == null || hashKeySet.isEmpty()) {
            return Collections.emptyMap();
        }
        final Map<String, String> resultMap = new HashMap<>(hashKeySet.size());
        hashKeySet.forEach(hashKey -> resultMap.put(hashKey, hashGet(redisKey, hashKey)));
        return resultMap;
    }

    /**
     * 查询redisKey所对应的所有的值并转换为对应clazz类型的bean
     *
     * @param redisKey 缓存key
     * @param clazz    bean类型
     * @return 查询到的值
     */
    @Nullable
    public <T> T hashGet(String redisKey, Class<T> clazz) {
        Map<String, String> map = getHashOperations().entries(redisKey);
        if (!map.isEmpty()) {
            return JSON.parseObject(JSON.toJSONString(map), clazz);
        }
        return null;
    }

    /**
     * 查询redisKey下的所有子项hashKey对应的值，当获取的hash的值类型大于一个时使用该方法
     *
     * @param redisKey 缓存key
     * @param classes  缓存值的类型数组，越靠前的类型优先级越高
     * @return 查询到的值
     */
    @NonNull
    public Map<String, Object> hashGetAll(String redisKey, Class<?>... classes) {
        final Map<String, Object> resultMap = new HashMap<>(64);
        getHashOperations().entries(redisKey).forEach((hashKey, value) -> resultMap.put(hashKey, parseObject(value, classes)));
        return resultMap;
    }

    /**
     * 查询redisKey下的所有子项hashKey对应的值，当获取的hash的值类型等于一个时使用该方法
     *
     * @param redisKey 缓存key
     * @param clazz    缓存值的类型
     * @return 查询到的值
     */
    @NonNull
    public <T> Map<String, T> hashGetAll(String redisKey, Class<T> clazz) {
        final Map<String, T> resultMap = new HashMap<>(64);
        getHashOperations().entries(redisKey).forEach((hashKey, value) -> resultMap.put(hashKey, parseObject(value, clazz)));
        return resultMap;
    }

    /**
     * 查询redisKey下的所有子项hashKey对应的值，当获取的hash的值类型等于一个时使用该方法
     *
     * @param redisKey 缓存key
     * @param clazz    list中保存的类型
     * @return 查询到的值
     */
    @NonNull
    public <T> Map<String, List<T>> hashGetAllArray(String redisKey, Class<T> clazz) {
        final Map<String, List<T>> resultMap = new HashMap<>(64);
        getHashOperations().entries(redisKey).forEach((hashKey, value) -> resultMap.put(hashKey, parseArray(value, clazz)));
        return resultMap;
    }

    /**
     * 查询redisKey下的所有子项hashKey对应的值，当获取的hash的值类型等于一个时使用该方法
     *
     * @param redisKey 缓存key
     * @return 查询到的值
     */
    @NonNull
    public Map<String, String> hashGetAll(String redisKey) {
        final Map<String, String> resultMap = new HashMap<>(64);
        getHashOperations().entries(redisKey).forEach(resultMap::put);
        return resultMap;
    }

    /**
     * 查询redisKey下的所有子项hashKey对应的值，当获取的hash的值类型大于一个时使用该方法
     *
     * @param redisKeySet 缓存key集合
     * @param classes     缓存值的类型数组，越靠前的类型优先级越高
     * @return 查询到的值
     */
    @NonNull
    public Map<String, Map<String, Object>> hashGetAll(Set<String> redisKeySet, Class<?>... classes) {
        final Map<String, Map<String, Object>> resultMap = new HashMap<>(redisKeySet.size());
        redisKeySet.forEach(redisKey -> resultMap.put(redisKey, hashGetAll(redisKey, classes)));
        return resultMap;
    }

    /**
     * 查询redisKey下的所有子项hashKey对应的值，当获取的hash的值类型等于一个时使用该方法
     *
     * @param redisKeySet 缓存key集合
     * @param clazz       缓存值的类型
     * @return 查询到的值
     */
    @NonNull
    public <T> Map<String, Map<String, T>> hashGetAll(Set<String> redisKeySet, Class<T> clazz) {
        Map<String, Map<String, T>> resultMap = new HashMap<>(redisKeySet.size());
        redisKeySet.forEach(redisKey -> resultMap.put(redisKey, hashGetAll(redisKey, clazz)));
        return resultMap;
    }

    /**
     * 查询redisKey下的所有子项hashKey对应的值，当获取的hash的值类型等于一个时使用该方法
     *
     * @param redisKeySet 缓存key集合
     * @return 查询到的值
     */
    @NonNull
    public Map<String, Map<String, String>> hashGetAll(Set<String> redisKeySet) {
        Map<String, Map<String, String>> resultMap = new HashMap<>(redisKeySet.size());
        redisKeySet.forEach(redisKey -> resultMap.put(redisKey, hashGetAll(redisKey)));
        return resultMap;
    }

    /**
     * 查询redisKey下的所有子项hashKey
     *
     * @param redisKey 缓存key
     * @return 查询到的所有的hashKey集合
     */
    @NonNull
    public Set<String> hashKeys(String redisKey) {
        try {
            Set<String> resultSet = getHashOperations().keys(redisKey);
            if (!resultSet.isEmpty()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Find hashKey set({}) from redis key({})!", resultSet, redisKey);
                }
                return resultSet;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Did't find hashKey set from redis key({})!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Find hashKey set from redis key({}) failure!", redisKey, e);
        }
        return new HashSet<>();
    }

    /**
     * 查询数组redisKeyArray下的所有子项hashKey
     *
     * @param redisKeySet 缓存key集合
     * @return 查询到的所有的hashKey集合
     */
    @NonNull
    public Map<String, Set<String>> hashKeys(Set<String> redisKeySet) {
        final Map<String, Set<String>> resultMap = new HashMap<>(redisKeySet.size());
        redisKeySet.forEach(redisKey -> resultMap.put(redisKey, hashKeys(redisKey)));
        return resultMap;
    }

    /**
     * 查询redis中redisKey下的子项hashKey是否有值
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @return 查询到的值
     */
    public boolean hashHasKey(String redisKey, String hashKey) {
        try {
            return getHashOperations().hasKey(redisKey, hashKey);
        } catch (Exception e) {
            logger.error("Find object from redis key({}) hashKey({}) failure!", redisKey, hashKey, e);
        }
        return false;
    }

    /**
     * 查询redis中redisKey下的子项hashKey是否有值
     *
     * @param redisKey   缓存key
     * @param hashKeySet 子项
     * @return 查询到的值
     */
    @NonNull
    public Map<String, Boolean> hashHasKey(String redisKey, Set<String> hashKeySet) {
        final Map<String, Boolean> resultMap = new HashMap<>(hashKeySet.size());
        hashKeySet.forEach(hashKey -> resultMap.put(hashKey, hashHasKey(redisKey, hashKey)));
        return resultMap;
    }

    /**
     * 查询hash型的size，如果查询不到则返回0，返回null表示查询异常
     *
     * @param redisKey 缓存key
     * @return list类型的长度
     */
    @Nullable
    public Long hashSize(String redisKey) {
        Long result = null;
        try {
            result = getHashOperations().size(redisKey);
            if (result == null) {
                logger.error("Get hash length from redis key({}) failure!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Get hash length from redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * 删除redisKey下的子项hashKey对应的值
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @return true:删除成功,false:删除失败
     */
    public boolean hashDelete(String redisKey, String hashKey) {
        try {
            getHashOperations().delete(redisKey, hashKey);
            if (logger.isDebugEnabled()) {
                logger.debug("Remove redis cache key({}) hashKey({}) success!", redisKey, hashKey);
            }
            return true;
        } catch (Exception e) {
            logger.error("Remove redis cache key({}) hashKey({}) failure!", redisKey, hashKey, e);
        }
        return false;
    }

    /**
     * 删除redisKey下的子项hashKey对应的值
     *
     * @param redisKey   缓存key
     * @param hashKeySet 子项
     * @return true:删除成功,false:删除失败
     */
    public boolean hashDelete(String redisKey, Set<String> hashKeySet) {
        if (hashKeySet == null || hashKeySet.isEmpty()) {
            return true;
        }
        try {
            hashKeySet.forEach(hashKey -> hashDelete(redisKey, hashKey));
            if (logger.isDebugEnabled()) {
                logger.debug("Remove redis cache key({}) hashKeyset({}) success!", redisKey, hashKeySet);
            }
            return true;
        } catch (Exception e) {
            logger.error("Remove redis cache key({}) hashKeyset({}) failure!", redisKey, hashKeySet, e);
        }
        return false;
    }

    /**
     * 为redisKey下的子项hashKey对应的整数值加上增量increment
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @param delta    增量值
     * @return 增加成功后的值
     */
    public Long hashIncrement(String redisKey, String hashKey, long delta) {
        try {
            Long result = getHashOperations().increment(redisKey, hashKey, delta);
            if (logger.isDebugEnabled()) {
                logger.debug("Increment redis cache key({}) hashKey({}) success, the result is {}!", redisKey, hashKey, result);
            }
            return result;
        } catch (Exception e) {
            logger.error("Increment redis cache key({}) hashKey({}) failure!", redisKey, hashKey, e);
        }
        return null;
    }

    /**
     * 为redisKey下的子项hashKey对应的浮点数值加上增量increment
     *
     * @param redisKey 缓存key
     * @param hashKey  子项
     * @param delta    增量值
     * @return 增加成功后的值
     */
    public Double hincrByFloat(String redisKey, String hashKey, double delta) {
        try {
            Double result = getHashOperations().increment(redisKey, hashKey, delta);
            if (logger.isDebugEnabled()) {
                logger.debug("Increment redis cache key({}) hashKey({}) success, the result is {}!", redisKey, hashKey, result);
            }
            return result;
        } catch (Exception e) {
            logger.error("Increment redis cache key({}) hashKey({}) failure!", redisKey, hashKey, e);
        }
        return null;
    }

    /**
     * 将一个值 value 插入到列表 key 的表头
     *
     * @param redisKey 缓存key
     * @param value    保存的对象
     * @return 列表的长度
     */
    public Long listLeftPush(String redisKey, Object value) {
        Long result = -1L;
        try {
            result = getListOperations().leftPush(redisKey, getValueStr(value));
            if (result == null) {
                logger.error("Left push list to redis key({}) failure!", redisKey);
                return -1L;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Left push list to redis key({}) success, length: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Left push list to redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * 将多个值 value 插入到列表 key 的表头
     *
     * @param redisKey 缓存key
     * @param list     保存的对象列表
     * @return 列表的长度
     */
    public Long listLeftPushAll(String redisKey, List<String> list) {
        Long result = -1L;
        try {
            result = getListOperations().leftPushAll(redisKey, list);
            if (result == null) {
                logger.error("Left push list to redis key({}) failure!", redisKey);
                return -1L;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Left push list to redis key({}) success, length: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Left push list to redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * 将一个值 value 插入到列表 key 的表尾
     *
     * @param redisKey 缓存key
     * @param value    保存的对象
     * @return 列表的长度
     */
    public Long listRightPush(String redisKey, Object value) {
        Long result = -1L;
        try {
            result = getListOperations().rightPush(redisKey, getValueStr(value));
            if (result == null) {
                logger.error("Right push list to redis key({}) failure!", redisKey);
                return -1L;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Right push list to redis key({}) success, length: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Right push list to redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * 将多个值 value 插入到列表 key 的表尾
     *
     * @param redisKey 缓存key
     * @param list     保存的对象列表
     * @return 列表的长度
     */
    public Long listRightPushAll(String redisKey, List<String> list) {
        Long result = -1L;
        try {
            result = getListOperations().rightPushAll(redisKey, list);
            if (result == null) {
                logger.error("Right push list to redis key({}) failure!", redisKey);
                return -1L;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Right push list to redis key({}) success, length: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Right push list to redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * 返回列表 key 中指定区间内的元素，区间以偏移量 start 和 stop 指定。
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     * 假如你有一个包含一百个元素的列表，对该列表执行 LRANGE list 0 10 ，结果是一个包含11个元素的列表
     * 超出范围的下标值不会引起错误。
     * 如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大，那么 LRANGE 返回一个空列表。
     * 如果 stop 下标比 end 下标还要大，Redis将 stop 的值设置为 end 。
     *
     * @param key   redisKey
     * @param index list 开始 下标
     * @param end   list 结束的下
     * @param clazz 转化的对象
     * @param <T>   泛型
     * @return list
     */
    public <T> List<T> listLeftRange(String key, Long index, Long end, Class<T> clazz) {
        final List<T> resultList = new ArrayList<>();
        getListOperations().range(key, index, end).forEach(str -> resultList.add(parseObject(str, clazz)));
        return resultList;
    }

    /**
     * 返回列表 key 中指定区间内的元素，区间以偏移量 start 和 stop 指定。
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     * 假如你有一个包含一百个元素的列表，对该列表执行 LRANGE list 0 10 ，结果是一个包含11个元素的列表
     * 超出范围的下标值不会引起错误。
     * 如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大，那么 LRANGE 返回一个空列表。
     * 如果 stop 下标比 end 下标还要大，Redis将 stop 的值设置为 end 。
     *
     * @param redisKey redisKey
     * @param start    list 开始 下标
     * @param end      list 结束的下标
     * @return 获取到的列表
     */
    public List<String> listLeftRange(String redisKey, Long start, Long end) {
        try {
            List<String> result = getListOperations().range(redisKey, start, end);
            if (result == null) {
                logger.error("Get list range from redis key({}) failure, start: {}, end: {}!", redisKey, start, end);
                return Collections.emptyList();
            } else if (logger.isDebugEnabled()) {
                logger.debug("Get list range from redis key({}) success, start: {}, end: {}, value: {}!",
                        redisKey, start, end, getValueStr(result));
            }
            return result;
        } catch (Exception e) {
            logger.error("Get list range from redis key({}) failure, start: {}, end: {}!", redisKey, start, end, e);
        }
        return Collections.emptyList();
    }

    /**
     * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     * 当 key 不是列表类型时，返回一个错误。
     *
     * @param redisKey 缓存key
     * @param index    list的开始下标
     * @param end      list的结束下标
     */
    public void listLeftTrim(String redisKey, Long index, Long end) {
        try {
            getListOperations().trim(redisKey, index, end);
        } catch (Exception e) {
            logger.error("Trim list from redis key({}) failure!", redisKey, e);
        }
    }

    public String listLeftPop(String redisKey) {
        try {
            String result = getListOperations().leftPop(redisKey);
            if (StringUtils.isNotEmpty(result)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Find object from redis key({}) success, value: {}!", redisKey, result);
                }
                return result;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Did't find object from redis key({})!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Find object from redis key({}) failure!", redisKey, e);
        }
        return null;
    }

    /**
     * 从左侧获取并删除一个list类型的值
     *
     * @param redisKey 缓存key
     * @param clazz    要转换的类型
     * @return list类型的值
     */
    public <T> T listLeftPop(String redisKey, Class<T> clazz) {
        return parseObject(listLeftPop(redisKey), clazz);
    }

    /**
     * 从右侧获取并删除一个list类型的值
     *
     * @param redisKey 缓存key
     * @return list类型的值
     */
    public String listRightPop(String redisKey) {
        try {
            String result = getListOperations().rightPop(redisKey);
            if (StringUtils.isNotEmpty(result)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Find object from redis key({}) success, value: {}!", redisKey, result);
                }
                return result;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Did't find object from redis key({})!", redisKey);
            }
        } catch (Exception e) {
            logger.error("Find object from redis key({}) failure!", redisKey, e);
        }
        return null;
    }

    /**
     * 从右侧获取并删除一个list类型的值
     *
     * @param redisKey 缓存key
     * @param clazz    要转换的类型
     * @return list类型的值
     */
    public <T> T listRightPop(String redisKey, Class<T> clazz) {
        return parseObject(listRightPop(redisKey), clazz);
    }

    /**
     * 查询list类型的size，如果查询不到则返回0，返回null表示查询异常
     *
     * @param redisKey 缓存key
     * @return list类型的长度
     */
    @Nullable
    public Long listSize(String redisKey) {
        Long result = null;
        try {
            result = getListOperations().size(redisKey);
            if (result == null) {
                logger.error("Get list length from redis key({}) failure!", redisKey);
            } else if (logger.isDebugEnabled()) {
                logger.debug("Get list length from redis key({}) success, value: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Get list length from redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * Set增加元素
     *
     * @param redisKey 缓存key
     * @param object   缓存对象
     * @return 增加成功的长度
     */
    public Long setAdd(String redisKey, Object object) {
        Long result = null;
        try {
            result = getSetOperations().add(redisKey, getValueStr(object));
            if (result == null) {
                logger.error("Set redis key({}) failure!", redisKey);
            } else if (logger.isDebugEnabled()) {
                logger.debug("Set redis key({}) success, length: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Set redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * Set增加元素
     *
     * @param redisKey   缓存key
     * @param collection 缓存对象集合
     * @return 增加成功的长度
     */
    public Long setAdd(String redisKey, Collection<?> collection) {
        if (collection == null || collection.isEmpty()) {
            return 0L;
        }
        Long result = null;
        try {
            result = getSetOperations().add(redisKey,
                    collection.stream()
                            .map(this::getValueStr)
                            .distinct()
                            .toArray(String[]::new));
            if (result == null) {
                logger.error("Set redis key({}) failure!", redisKey);
            } else if (logger.isDebugEnabled()) {
                logger.debug("Set redis key({}) success, length: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Set redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * 获取redis中指定key的SET集合
     *
     * @param redisKey 缓存key
     * @return 指定key的SET集合
     */
    @NonNull
    public Set<String> setMembers(String redisKey) {
        try {
            Set<String> result = getSetOperations().members(redisKey);
            if (result == null) {
                logger.error("Get set members from redis key({}) failure!", redisKey);
                return Collections.emptySet();
            } else if (logger.isDebugEnabled()) {
                logger.debug("Get set members from redis key({}) success, value: {}!", redisKey, getValueStr(result));
            }
            return result;
        } catch (Exception e) {
            logger.error("Get set members from redis key({}) failure!", redisKey, e);
        }
        return Collections.emptySet();
    }

    /**
     * 获取redis中指定key的SET集合
     *
     * @param redisKey 缓存key
     * @return 指定key的SET集合
     */
    @NonNull
    public <T> Set<T> setMembers(String redisKey, Class<T> clazz) {
        try {
            Set<String> resultStr = getSetOperations().members(redisKey);
            if (resultStr == null) {
                logger.error("Get set members from redis key({}) failure!", redisKey);
                return Collections.emptySet();
            } else if (logger.isDebugEnabled()) {
                logger.debug("Get set members from redis key({}) success, value: {}!", redisKey, getValueStr(resultStr));
            }
            return resultStr.stream().map(str -> parseObject(str, clazz)).collect(Collectors.toSet());
        } catch (Exception e) {
            logger.error("Get set members from redis key({}) failure!", redisKey, e);
        }
        return Collections.emptySet();
    }

    /**
     * 移除并返回集合中的一个随机元素。
     * 如果只想获取一个随机元素，但不想该元素从集合中被移除的话，可以使用 SRANDMEMBER 命令。
     *
     * @param redisKey 缓存key
     * @return 集合中的一个随机元素
     */
    @Nullable
    public String setPop(String redisKey) {
        String result = null;
        try {
            result = getSetOperations().pop(redisKey);
            if (logger.isDebugEnabled()) {
                logger.debug("Pop set members from redis key({}) success, value: {}!", redisKey, result);
            }
        } catch (Exception e) {
            logger.error("Pop set member from redis key({}) failure!", redisKey, e);
        }
        return result;
    }

    /**
     * 移除并返回集合中的一个随机元素。
     * 如果只想获取一个随机元素，但不想该元素从集合中被移除的话，可以使用 SRANDMEMBER 命令。
     *
     * @param redisKey 缓存key
     * @return 集合中的一个随机元素
     */
    @Nullable
    public <T> T setPop(String redisKey, Class<T> clazz) {
        try {
            String result = getSetOperations().pop(redisKey);
            if (logger.isDebugEnabled()) {
                logger.debug("Pop set members from redis key({}) success, value: {}!", redisKey, result);
            }
            return parseObject(result, clazz);
        } catch (Exception e) {
            logger.error("Pop set member from redis key({}) failure!", redisKey, e);
        }
        return null;
    }

    /**
     * 执行redis脚本
     *
     * @param script 脚本
     * @param keys   需要用到的key
     * @param args   需要用到的参数
     * @return 执行结果
     */
    @SuppressWarnings("unchecked")
    public String execute(String script, List<String> keys, List<String> args) {
        return execute(redisScriptMap.computeIfAbsent(script, key -> RedisScript.of(key, String.class)), keys, args);
    }

    /**
     * 执行redis脚本
     *
     * @param script 脚本
     * @param keys   需要用到的key
     * @param args   需要用到的参数
     * @return 执行结果
     */
    public String execute(RedisScript<String> script, List<String> keys, List<String> args) {
        try {
            String result = redisTemplate.execute(script, keys, args.toArray());
            if (logger.isDebugEnabled()) {
                logger.debug("Execute redis script({}) success!", script.getScriptAsString());
            }
            return result;
        } catch (Exception e) {
            logger.error("Execute redis script({}) failure!", script.getScriptAsString(), e);
        }
        return null;
    }

    /**
     * 获取redisKey值(加前缀，前缀由easy-ms.redis.prefix设定)
     *
     * @param args 参数
     * @return 带前缀的redisKey值
     */
    public String getRedisKeyWithPrefix(String... args) {
        String redisKey = getRedisKeyWithoutPrefix(args);
        String prefix = easyMsRedisProperties.getPrefix();
        if (StringUtils.isBlank(prefix)) {
            return redisKey;
        }
        return StringUtils.join(new Object[]{prefix, redisKey}, EasyMsCommonConstants.REDIS_KEY_SEPARATOR);
    }

    /**
     * 获取redisKey值(不加前缀)
     *
     * @param args 参数
     * @return 不带前缀的redisKey值
     */
    public String getRedisKeyWithoutPrefix(String... args) {
        List<String> list = new ArrayList<>();
        for (String arg : args) {
            if (arg != null) {
                list.add(arg);
            }
        }
        return StringUtils.join(list.toArray(), EasyMsCommonConstants.REDIS_KEY_SEPARATOR);
    }

    /**
     * 将一个字符串转换成对应的类型
     *
     * @param text  字符串
     * @param clazz 待转换的类型
     * @return 转换后的对象
     */
    @SuppressWarnings("unchecked")
    public <T> T parseObject(String text, Class<T> clazz) {
        if (text == null || clazz == null) {
            return null;
        }
        if (clazz == String.class) {
            return (T) text;
        }
        try {
            return JSON.parseObject(text, clazz);
        } catch (Exception e) {
            logger.error("String '{}' parse Class({}) failure!", text, clazz.getName(), e);
        }
        return null;
    }

    /**
     * 将一个字符串转换成对应的类型列表
     *
     * @param text  字符串
     * @param clazz 待转换的类型
     * @return 转换后的对象列表
     */
    public <T> List<T> parseArray(String text, Class<T> clazz) {
        if (text != null) {
            try {
                return JSON.parseArray(text, clazz);
            } catch (Exception e) {
                logger.error("String '{}' parse Class(List<{}>) failure!", text, clazz.getName(), e);
            }
        }
        return new ArrayList<>();
    }

    /**
     * 将json字符串转换成class对象，若多个class时选择最符合的class进行转换
     *
     * @param text    json字符串
     * @param classes class集合
     * @return 转换后的对象
     */
    public Object parseObject(String text, Class<?>... classes) {
        if (text != null && classes != null) {
            if (classes.length == 1) {
                Class<?> clazz = classes[0];
                try {
                    return JSON.parseObject(text, clazz);
                } catch (Exception e) {
                    logger.error("String '{}' parse Class({}) failure! {}", text, clazz.getName(), e);
                }
            } else if (classes.length > 1) {
                // 如果无法转换成Map类型则是基础数据类型或String类型，直接返回
                HashMap parseMap;
                try {
                    parseMap = JSON.parseObject(text, HashMap.class);
                } catch (Exception e) {
                    return text;
                }
                if (parseMap == null || parseMap.isEmpty()) {
                    return text;
                }
                // 将对象转换成最匹配的类型
                Class<?> bestMatchClass = null;
                int bestMatchFieldCount = 0;
                for (Class<?> clazz : classes) {
                    int currentMatchFieldCount = 0;
                    Field[] hashKeys = clazz.getDeclaredFields();
                    for (Field hashKey : hashKeys) {
                        if (parseMap.get(hashKey.getName()) != null) {
                            currentMatchFieldCount++;
                        }
                    }
                    if (currentMatchFieldCount > bestMatchFieldCount) {
                        bestMatchClass = clazz;
                        bestMatchFieldCount = currentMatchFieldCount;
                    }
                }
                if (bestMatchClass == null) {
                    return text;
                }
                try {
                    return JSON.parseObject(text, bestMatchClass);
                } catch (Exception e) {
                    logger.error("String '{}' parse Class({}) failure!", text, bestMatchClass.getName(), e);
                }
            }
        }
        return text;
    }

    /**
     * 将一个Object对象转换为String对象
     *
     * @param value 待转换的值
     * @return String对象
     */
    public String getValueStr(Object value) {
        if (value instanceof String) {
            return (String) value;
        }
        return JSON.toJSONString(value);
    }

    @NonNull
    public ValueOperations<String, String> getValueOperations() {
        ValueOperations<String, String> localValueOperations = valueOperations;
        if (localValueOperations == null) {
            synchronized (redisTemplate) {
                localValueOperations = valueOperations;
                if (localValueOperations == null) {
                    valueOperations = localValueOperations = redisTemplate.opsForValue();
                }
            }
        }
        return localValueOperations;
    }

    @NonNull
    public HashOperations<String, String, String> getHashOperations() {
        HashOperations<String, String, String> localHashOperations = hashOperations;
        if (localHashOperations == null) {
            synchronized (redisTemplate) {
                localHashOperations = hashOperations;
                if (localHashOperations == null) {
                    hashOperations = localHashOperations = redisTemplate.opsForHash();
                }
            }
        }
        return localHashOperations;
    }

    @NonNull
    public ListOperations<String, String> getListOperations() {
        ListOperations<String, String> localListOperations = listOperations;
        if (localListOperations == null) {
            synchronized (redisTemplate) {
                localListOperations = listOperations;
                if (localListOperations == null) {
                    listOperations = localListOperations = redisTemplate.opsForList();
                }
            }
        }
        return localListOperations;
    }

    @NonNull
    public SetOperations<String, String> getSetOperations() {
        SetOperations<String, String> localSetOperations = setOperations;
        if (localSetOperations == null) {
            synchronized (redisTemplate) {
                localSetOperations = setOperations;
                if (localSetOperations == null) {
                    setOperations = localSetOperations = redisTemplate.opsForSet();
                }
            }
        }
        return localSetOperations;
    }

    public EasyMsRedisTemplate(EasyMsRedisProperties easyMsRedisProperties, RedisTemplate<String, String> redisTemplate) {
        this.easyMsRedisProperties = easyMsRedisProperties;
        this.redisTemplate = redisTemplate;
    }

}
